package com.ctgu.project.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctgu.project.entity.ShopsEntity;
import com.ctgu.project.mapper.ShopsMapper;
import com.ctgu.project.service.ShopsService;
import com.ctgu.project.utils.ApiResult;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author Alex2
* @description 针对表【t_shops】的数据库操作Service实现
* @createDate 2025-02-27 10:34:14
*/
@Log4j2
@Service
public class ShopsServiceImpl extends ServiceImpl<ShopsMapper, ShopsEntity>
    implements ShopsService {

    private final StringRedisTemplate redisTemplate;

    @Autowired
    public ShopsServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    private static final String TAG = "Alex";

    private static final String SHOP_GEO_KEY = "shop:geo:";

    /**
     * @Author: Alex
     * @Description: 像数据库中插入商铺信息
     */
    @Override
    public ApiResult insertShopInfo() {
        ShopsEntity shop1 = new ShopsEntity(null, "海底捞", 1, 120.149192, 30.316078, null);
        ShopsEntity shop2 = new ShopsEntity(null, "重庆火锅", 1, 121.153245, 30.125323, null);
        ShopsEntity shop3 = new ShopsEntity(null, "好邻居", 2, 120.246547, 29.543762, null);
        ShopsEntity shop4 = new ShopsEntity(null, "中百罗森", 2, 121.134345, 30.654564, null);
        ShopsEntity shop5 = new ShopsEntity(null, "蜜雪冰城", 3, 120.844534, 31.122131, null);
        ShopsEntity shop6 = new ShopsEntity(null, "古茗", 3, 120.134534, 30.234234, null);

        List<ShopsEntity> list = Arrays.asList(shop1, shop2, shop3, shop4, shop5, shop6);

        // 先检查所有商户是否已存在
        List<ShopsEntity> notExistingShops = new ArrayList<>();
        for (ShopsEntity shop : list) {
            ShopsEntity shopsEntity = lambdaQuery().eq(ShopsEntity::getLng, shop.getLng()).one();
            if (shopsEntity == null) {
                notExistingShops.add(shop);
            }
        }
        if (notExistingShops.isEmpty()) {
            log.error("{} : 所有商户均已存在，批量插入失败 ", TAG);
            return ApiResult.error("所有商户均已存在，批量插入失败");
        }
        for (ShopsEntity shops : notExistingShops) {
            save(shops);
        }
        log.info("批量插入以下商户 : {}", notExistingShops);
        return ApiResult.success("批量插入以下商户", notExistingShops);
    }

    /**
     * @Author: Alex
     * @Description: 将商铺信息加载到 Redis，根据商铺类型分类
     */
    @Override
    public ApiResult loadShopData() {
        List<ShopsEntity> list = query().list();
        // 把店铺分组，按照typeId分组，typeId一致的放到一个集合
        Map<Integer, List<ShopsEntity>> collect = list.stream().collect(Collectors.groupingBy(ShopsEntity::getTypeId));
        for (Map.Entry<Integer, List<ShopsEntity>> entry : collect.entrySet()) {
            ArrayList<RedisGeoCommands.GeoLocation<String>> geoLocations = new ArrayList<>();
            // 获取同类型的店铺的集合
            List<ShopsEntity> value = entry.getValue();
            // 写入redis GEOADD key 经度 纬度 member
            for (ShopsEntity shop : value) {
                Point point = new Point(shop.getLng(), shop.getLat());
                RedisGeoCommands.GeoLocation<String> stringGeoLocation = new RedisGeoCommands.GeoLocation<>(shop.getId().toString(), point);
                geoLocations.add(stringGeoLocation);
            }
            Integer typeId = entry.getKey();
            log.info("{} : Type = {} : {}", TAG, typeId, geoLocations);
            redisTemplate.opsForGeo().add(SHOP_GEO_KEY + typeId, geoLocations);
        }
        log.info("{} : {}", TAG, "商户信息成功加载到Redis");
        return ApiResult.success("商户信息成功加载到Redis");
    }

    /**
     * @Author: Alex
     * @Description: 查询附近商铺的信息
     */
    @Override
    public ApiResult getShopByType(Integer typeId, Double lng, Double lat, Long distanceMeter) {
        // 如果没有坐标参数，直接根据数据库查询
        if (lat == null || lng == null) {
            List<ShopsEntity> shopsEntities = query().eq("type_id", typeId).list();
            return ApiResult.success("获取附近商户成功", shopsEntities);
        }

        // 通过 Redis 获取附近的商户
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().search(
                SHOP_GEO_KEY + typeId,
                GeoReference.fromCoordinate(lng, lat),  // 查询以给定的经纬度为中心的圆形区域
                new Distance(distanceMeter),            // 查询100km范围内的店铺，单位默认为米
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance());

        // 如果没有获取到商户列表
        if (results == null || results.getContent().isEmpty()) {
            return ApiResult.success("获取附近商户列表为空", Collections.emptyList());
        }

        // 解析 Redis 查询结果
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
        ArrayList<Integer> ids = new ArrayList<>();
        HashMap<String, Distance> distanceHashMap = new HashMap<>();

        list.forEach(result -> {
            String id = result.getContent().getName();
            ids.add(Integer.parseInt(id));
            Distance distance = result.getDistance();
            distanceHashMap.put(id, distance);
            log.info("{} : id = {} : {}", TAG, id,distance);
        });

        // 根据商户 ID 查询数据库
        List<ShopsEntity> shopsEntities = new ArrayList<>();
        for (Integer id : ids) {
            ShopsEntity shop = query().eq("id", id).one();
            if (shop != null) {
                // 设置距离
                Distance distance = distanceHashMap.get(shop.getId().toString());
                if (distance != null) {
                    shop.setDistance(distance.getValue());
                }
                shopsEntities.add(shop);
            }
        }

        return ApiResult.success("获取" + distanceMeter / 1000 + "KM内的商铺成功", shopsEntities);
    }
}